package com.zzyl.serve.service.impl;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.RedisConstants;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.serve.dto.DeviceDto;
import com.zzyl.serve.vo.DeviceDetailVo;
import com.zzyl.serve.vo.ProductVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Service;
import com.zzyl.serve.mapper.DeviceMapper;
import com.zzyl.serve.domain.Device;
import com.zzyl.serve.service.IDeviceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

/**
 * 设备Service业务层处理
 *
 * @author baefengk2
 * @date 2025-03-14
 */
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService {

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private IoTDAClient client;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void syncProductList() {
        // 1. 从物联网平台获取产品信息
        ListProductsRequest listProductsRequest = new ListProductsRequest();
        // 分页查询，每页50条数据
        listProductsRequest.setLimit(50);
        // 获取产品列表
        ListProductsResponse response = client.listProducts(listProductsRequest);
        List<ProductSummary> list = response.getProducts();
        if (ObjectUtil.isEmpty(list)) {
            throw new BaseException("同步产品数据失败");
        }

        // 2. 将产品信息保存到Redis中
        redisTemplate.opsForValue().set(RedisConstants.IOT_ALL_PRODUCT_LIST, list);

    }

    /**
     * 查询所有产品列表
     *
     * @return
     */
    @Override
    public List<ProductVo> allProduct() {
        // 从Redis中获取产品列表
        List<ProductSummary> productList = (List<ProductSummary>) redisTemplate.opsForValue().get(RedisConstants.IOT_ALL_PRODUCT_LIST);

        if (ObjectUtil.isEmpty(productList)) {
            return CollectionUtil.empty(List.class);
        }

        // 拷贝 hutool BeanUtil
        List<ProductVo> list = BeanUtil.copyToList(productList, ProductVo.class);

        return list;
    }

    /**
     * 注册设备
     *
     * @param dto
     */
    @Override
    public void register(DeviceDto dto) {
        // 1. 判断设备是否已经注册
        // 1.1 设备名称是否重复
        long count1 = count(new LambdaQueryWrapper<Device>()
                .eq(StrUtil.isNotBlank(dto.getDeviceName()), Device::getDeviceName, dto.getDeviceName())
        );
        if (count1 > 0) {
            throw new BaseException("设备名称已存在，请重新输入");
        }

        // 1.2 设备标识是否重复
        long count2 = count(new LambdaQueryWrapper<Device>()
                .eq(StrUtil.isNotBlank(dto.getNodeId()), Device::getNodeId, dto.getNodeId())
        );
        if (count2 > 0) {
            throw new BaseException("设备标识码已存在，请重新输入");
        }

        // 1.3 同一位置是否绑定了相同的设备
        long count3 = count(new LambdaQueryWrapper<Device>()
                .eq(Device::getBindingLocation, dto.getBindingLocation())
                .eq(Device::getLocationType, dto.getLocationType())
                .eq(Device::getPhysicalLocationType, dto.getPhysicalLocationType())
        );
        if (count3 > 0) {
            throw new BaseException("该位置已绑定了设备，请重新输入");
        }

        // 2. 注册设备, 调用物联网平台API
        AddDeviceRequest request = new AddDeviceRequest();
        AddDevice body = new AddDevice();

        // 随机生成密钥
        String secret = UUID.randomUUID().toString().replace("-", "");

        // 设备认证信息
        AuthInfo authInfobody = new AuthInfo();
        authInfobody.withAuthType("SECRET")
                .withSecret(secret);

        body.withDescription(dto.getDeviceDescription());
        body.withAuthInfo(authInfobody);
        body.withProductId(dto.getProductKey());// 产品id 必填
        body.withDeviceName(dto.getDeviceName());
        body.withNodeId(dto.getNodeId());// 设备标识码 必填

        request.withBody(body);

        AddDeviceResponse response = new AddDeviceResponse();
        try {
            response = client.addDevice(request);
        } catch (Exception e) {
            throw new BaseException("注册设备失败");
        }

        // 获取设备id
        String deviceId = response.getDeviceId();

        // 3.封装设备对象
        Device device = BeanUtil.toBean(dto, Device.class);

        device.setIotId(deviceId);
        device.setSecret(secret);

        // 4. 保存到数据库
        save(device);
    }

    /**
     * 查询设备列表
     *
     * @param device 设备
     * @return 设备
     */
    @Override
    public List<Device> selectDeviceList(Device device) {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 查询设备详情
     *
     * @param iotId
     * @return
     */
    @Override
    public DeviceDetailVo getDeviceDetail(String iotId) {
        // 1. 查询数据库中设备信息
        LambdaQueryWrapper<Device> lqw = new LambdaQueryWrapper<Device>()
                .eq(Device::getIotId, iotId);
        Device device = getOne(lqw);

        // 2. 查询IOT平台设备信息
        ShowDeviceRequest request = new ShowDeviceRequest();
        request.withDeviceId(iotId);

        ShowDeviceResponse response = new ShowDeviceResponse();
        try {
            response = client.showDevice(request);
        } catch (Exception e) {
            throw new BaseException("查询设备详情失败, 请联系管理员");
        }

        // 3. 封装Vo返回
        DeviceDetailVo detailVo = BeanUtil.toBean(device, DeviceDetailVo.class);
        if (ObjectUtil.isNotEmpty(response)) {
            detailVo.setDeviceStatus(response.getStatus());

            String activeTimeStr = response.getActiveTime();
            if (StrUtil.isNotBlank(activeTimeStr)) {
                // 格式化时间格式  iot 返回的时间格式：2023-03-09T09:05:09Z
                LocalDateTime activeTime = LocalDateTimeUtil.parse(activeTimeStr, DatePattern.UTC_MS_PATTERN);
                //日期时区转换
                activeTime = activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                        .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                        .toLocalDateTime();
                detailVo.setActiveTime(activeTime);

            }

        }

        return detailVo;

    }

    /**
     * 查看设备上报的数据
     *
     * @param iotId
     * @return
     */
    @Override
    public AjaxResult queryServiceProperties(String iotId) {

        // 1. 调用iot平台查询设备上报的数据
        ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
        request.withDeviceId(iotId);
        ShowDeviceShadowResponse response = client.showDeviceShadow(request);
        if (response.getHttpStatusCode() != 200) {
            throw new BaseException("调用物联网接口响应失败");
        }

        List<DeviceShadowData> shadow = response.getShadow();
        if (CollUtil.isEmpty(shadow)) {
            return AjaxResult.warn("设备暂无数据");
        }

        // 2. 准备返回数据
        Object properties = shadow.get(0).getReported().getProperties();
        JSONObject jsonObject = JSONUtil.parseObj(properties);

        // 获取事件上报的时间  iot 返回的时间格式：yyyyMMdd'T'HHmmss'Z'
        String activeTimeStr = shadow.get(0).getReported().getEventTime();
        LocalDateTime activeTime = LocalDateTimeUtil.parse(activeTimeStr, "yyyyMMdd'T'HHmmss'Z'");
        //日期时区转换
        LocalDateTime eventTime = activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                .toLocalDateTime();

        List<Map<String, Object>> list = new ArrayList<>();

        jsonObject.forEach((key, value) -> {
            Map<String, Object> map = new HashMap<>();
            map.put("functionId", key);
            map.put("value", value);
            map.put("eventTime", eventTime);
            list.add(map);
        });

        return AjaxResult.success(list);
    }

    /**
     * 修改设备
     * @param dto 设备
     */
    @Override
    public void edit(DeviceDto dto) {
        // 1. 更新iot平台设备信息
        UpdateDeviceRequest request = new UpdateDeviceRequest();
        request.withDeviceId(dto.iotId);
        UpdateDevice body = new UpdateDevice();
        AuthInfoWithoutSecret authInfobody = new AuthInfoWithoutSecret();
        authInfobody.withSecureAccess(true);

        body.withAuthInfo(authInfobody);
        body.withDescription(dto.getDeviceDescription());
        body.withDeviceName(dto.getDeviceName());
        request.withBody(body);

        try {
            UpdateDeviceResponse response = client.updateDevice(request);
        } catch (Exception e) {
            throw new BaseException("修改设备失败");
        }

        // 2. 拷贝dto到device中
        Device device = BeanUtil.toBean(dto, Device.class);

        // 3. 更新数据库
        updateById(device);

    }

    /**
     * 删除设备
     * @param iotId
     */
    @Override
    public void remove(String iotId) {
        // 1. 调用iot平台api删除设备
        DeleteDeviceRequest request = new DeleteDeviceRequest();
        request.withDeviceId(iotId);
        try {
            DeleteDeviceResponse response = client.deleteDevice(request);
        } catch (Exception e) {
            throw new BaseException("删除设备失败");
        }

        // 2. 删除数据库中设备信息
        LambdaQueryWrapper<Device> eq = new LambdaQueryWrapper<Device>()
                .eq(Device::getIotId, iotId);

        remove(eq);

    }

    /**
     * 查询产品详情(报警规则模块使用)
     * @param productKey
     * @return
     */
    @Override
    public AjaxResult queryProduct(String productKey) {

        if (StrUtil.isBlank(productKey)) {
            return AjaxResult.error("参数不能为空");
        }

        // 1. 调用iot平台api查询产品详情
        ShowProductRequest request = new ShowProductRequest();
        request.withProductId(productKey);
        try {
            // 发送请求
            ShowProductResponse response = client.showProduct(request);

            if (response.getHttpStatusCode() != 200) {
                throw new BaseException("查询产品详情失败");
            }

            // 2. 解析请求返回的数据
            List<ServiceCapability> serviceCapabilities = response.getServiceCapabilities();
            if (CollUtil.isNotEmpty(serviceCapabilities)){
                // 与前端需要的数据格式一致,直接返回数据
                return AjaxResult.success(serviceCapabilities);
            }

        } catch (Exception e){
            throw new BaseException("查询产品详情失败");
        }

        return AjaxResult.success(Collections.emptyList());
    }


}
